#!/usr/bin/env bash

function print_hr () {
    printf "%$(tput cols)s" | tr " " "-"
}


function print_ip_addr () {
    LANG=C /sbin/ifconfig | awk '
    # ifconfig output under different distros is placed in doc/ifconfig.md.
    BEGIN {
        skip = 0;
        has_ip = 0;
        line_count = 0;
        begpat_found = 0;
        endpat_found = 0;
    }

    /^(en|wl|ww|br|tun|eth|em|docker)/ {
        if (begpat_found == 0) {
            begpat_found = 1;
        } else if (begpat_found == 1) {
            range[++line_count] = SUBSEP;
            endpat_found = 0;
        }
        range[++line_count] = $0;
        next;
    }

    begpat_found == 1 {
        if ($0 ~ /^([[:space:]]+|$)/) {
            endpat_found = 1;
            range[++line_count] = $0;
        } else {
            if (endpat_found == 0) {
                range[++line_count] = $0;
            } else if (endpat_found == 1) {
                # time to end range part.
                range[++line_count] = SUBSEP;
                begpat_found = 0;
                endpat_found = 0;
            }
        }
    }

    END {
        # NOTE: for...in... is not reliable order, do not use it if you want
        # exact order in array!
        range_len = length(range);
        for (line_index = 1; line_index <= range_len; line_index++) {
            line = range[line_index];
            if (line != SUBSEP) {
                if (line ~ /^(en|wl|ww|br|tun|eth|em|docker)/) {
                    if (line !~ /^[[:alnum:]]+:avahi/) {
                        skip = 0;
                        has_ip = 0;
                        split(line, tmp_array);
                        if_name = tmp_array[1];
                        sub(/:$/, "", if_name);
                    } else {
                        skip = 1;
                    }
                    continue;
                }

                if (skip == 0 && \
                        line ~ /^[[:space:]]+inet[[:space:]]+(addr:)?[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) {
                    # regex {n,m} is supported in GNU Awk 3.1.5 of CentOS 5 with
                    # --posix option, so not used here.

                    has_ip = 1;
                    split(line, tmp_array);
                    if_ip = tmp_array[2];
                    sub(/addr:/, "", if_ip);
                    if_ips[if_name] = if_ip;
                    skip = 1;
                    continue;
                }

                if (skip == 0 && has_ip == 0) {
                    if_ip = "NO IP";
                    if_ips[if_name] = if_ip;
                    continue;
                }
            } else {
                skip = 0;
                has_ip = 0;
            }
        }

        # PROCINFO is GNU EXTENSIONS and varies in different version,
        # so not used here, use Insertion Sort instead.
        #
        # Sorting algorithms/Insertion sort - Rosetta Code
        # https://rosettacode.org/wiki/Sorting_algorithms/Insertion_sort#AWK

        i = 0
        for (if_name in if_ips) {
            i++;
            if_names[i] = if_name;
        }

        for (j = 1; j <= length(if_names); j++) {
            value = if_names[j];
            k = j - 1;
            while (( k > 0) && (if_names[k] > value)) {
                if_names[k + 1] = if_names[k];
                k--;
            }
            if_names[k + 1] = value;
        }

        for (x = 1; x <= length(if_names); x++) {
            if_name = if_names[x];
            ip_addrs = ip_addrs if_name " = " if_ips[if_name] "; ";
        }
        sub(/; $/, "", ip_addrs);
        printf ip_addrs;
    }
    '
}


function print_online_user () {
    # who -uH # under LANG=C
    #
    # NAME     LINE         TIME         IDLE          PID COMMENT
    # zach.zhang pts/1        Apr  7 13:04   .         14631 (116.231.138.115)

    # Keep same LANG when issue command for same output.
    LANG=C who -u | awk '
    function sub_array_str(arr, first_index,    second, element, tmp_array) {
        # standard AWK only support multidimensional arrays, arrays of arrays
        # only supported by GAWK is not supported in macOS. BTW., array is not
        # allowed to return in function, coz array could be recognized as
        # global variable.
        #
        # The GNU Awk User’s Guide: Multidimensional
        # https://www.gnu.org/software/gawk/manual/html_node/Multidimensional.html
        # The GNU Awk User’s Guide: Arrays of Arrays
        # https://www.gnu.org/software/gawk/manual/html_node/Arrays-of-Arrays.html

        for (element in arr) {
            split(element, tmp_array, SUBSEP);
            if (first_index == tmp_array[1]) {
                second = second SUBSEP tmp_array[2];
            }
        }
        # remove leading SUBSEP.
        sub(SUBSEP, "", second);
        return second;
    }

    BEGIN {
        total_online_user = 0;
        online_user_summary = "";
        # Color code
        color_reset = "\\e[0m";
        color_red = "\\e[31m";;
        color_green = "\\e[32m";
        color_yellow = "\\e[33m";
        color_blue = "\\e[34m";
        color_magenta = "\\e[35m";
    }

    {
        name = $1;
        pid = $7;
        comment = $8;    # (221.239.144.8)
        # only remember the last comment of same pid and name.
        who_name[name]++;
        who_pid[name, pid]++;
        who_comment[name, pid] = comment;
    }

    END {
        for (name in who_name) {
            total_online_user++;
            pids_str = sub_array_str(who_pid, name);
            split(pids_str, pids, SUBSEP);
            for (pid_index in pids) {
                pid = pids[pid_index];
                user_login[name]++;
                user_comment = who_comment[name, pid];
                if (user_comment ~ /^\([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\)$/) {
                    gsub(/^\(|\)$/, "", user_comment);
                    ip = user_comment;
                    user_remote[name]++;
                    user_remote_ip[name, ip]++;
                }
            }
        }
        online_user_summary = sprintf("%sOnline%s %s%d%s %susers%s",
                                      color_green, color_reset,
                                      color_red, total_online_user,
                                      color_reset, color_magenta,
                                      color_reset);
        for (name in user_login) {
            user_total_login = 0;
            user_total_remote_login = 0;
            user_total_local_login = 0;
            remote_ips = 0;
            if (name in user_remote) {
                ips_str = sub_array_str(user_remote_ip, name);
                split(ips_str, ips, SUBSEP);
                for (ip_index in ips) {
                    ip = ips[ip_index];
                    user_total_remote_login += user_remote_ip[name, ip];
                    remote_ips++;
                }
            }
            user_total_login = user_login[name];
            user_total_local_login = user_total_login - user_total_remote_login;
            # name (t: {user total login num},
            #       r: {user different remote ip num}_{user remote login num},
            #       l: {user local login num})
            # e.g.:
            # zach.zhang (t: 2, r: 1_2, l: 0)
            online_user_summary = sprintf("%s, %s (%st%s: %d, %sr%s: %d_%d, %sl%s: %d)",
                                          online_user_summary, name,
                                          color_magenta, color_reset,
                                          user_total_login, color_yellow, color_reset,
                                          remote_ips, user_total_remote_login,
                                          color_blue, color_reset,
                                          user_total_local_login);
        }
        printf online_user_summary;
    }
    '
}


# PS1
# Bash Prompt Escape Sequences
# http://tldp.org/HOWTO/Bash-Prompt-HOWTO/bash-prompt-escape-sequences.html
# \u: the username of the current user
# \H: the hostname
# \D{format}: formatted datetime
# \w: the current working directory
# \#: the command number of this command
# \$: if the effective UID is 0, a #, otherwise a $

# bash:tip_colors_and_formatting - FLOZz' MISC
# http://misc.flogisoft.com/bash/tip_colors_and_formatting
#COLOR_RESET="\[\e[0m\]"
#COLOR_GREEN="\[\e[32m\]"
#COLOR_YELLOW="\[\e[33m\]"
#COLOR_BLUE="\[\e[34m\]"
#COLOR_MAGENTA="\[\e[35m\]"
#COLOR_DEFAULT="\[\e[39m\]"

export PS1='$(print_hr)\n\n$(printf "$(print_online_user)")\[\e[0m\]\n[\[\e[34m\]\u\[\e[39m\]@\[\e[35m\]\H\[\e[0m\]]  \[\e[33m\]$(print_ip_addr)\[\e[0m\]\n[\[\e[33m\]\D{%Y-%m-%d %H:%M:%S}\[\e[0m]\] PWD => \[\e[35m\]\w\[\e[0m\]\n\#>\[\e[32m\]\$\[\e[0m\] '
